package com.googlecode.cardamomo.ermodel;

import java.security.InvalidParameterException;

import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlTransient;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;

import com.googlecode.cardamomo.jaxb.BooleanAdapter;
import com.googlecode.cardamomo.jaxb.IntegerAdapter;

/**
 * Column represents a database table's column.
 * 
 * @author jtorne
 *
 */

@XmlType(namespace=DataType.CARDAMOMO_NAMESPACE,
		propOrder={"name", "type", "size", "decimals", "nullable",
		"label", "signed", "required", "defaultValue", "comment"})

public class Column {

	@XmlTransient
	private Table table;
	
	private String name;	// Column name is case-sensitive
	private int size;	// maximum column size (in type units)
	private int decimals = 0; // for numeric columns, the maximum number of decimals
	private DataType type;
	private String label;	// Label represent an human-oriented, descriptive name for the column
	private boolean nullable = false;
	private String defaultValue;
	private String comment;
	private boolean primaryKey;
	private boolean required = false;
	
	/*=== Constructors ===*/
	
	public Column(String name) {
		setName(name);
	}

	public Column() {
	}
	
	/*=== Setters and getters ===*/
	
	@XmlElement(nillable=true)
	public String getComment() {
		return comment;
	}

	public void setComment(String comment) {
		this.comment = comment;
	}
	
	/**
	 * Nullable attribute relates to [0..1] multiplicity
	 * if true, multiplicity is [0..1], otherwise is [1]
	 * 
	 * @return
	 */
	@XmlAttribute(name="nullable")
	@XmlJavaTypeAdapter(type=Boolean.class, value = BooleanAdapter.class)
	public Boolean isNullable() {
		return nullable;
	}

	public void setNullable(Boolean nullable) {
		this.nullable = nullable;
	}

	/**
	 * Size is the length of the data type in units appropriate for the data type:
	 * bytes, digits, characters, bits...
	 * 
	 * @return
	 */
	@XmlAttribute(name="size")
	public int getSize() {
		return size;
	}

	public void setSize(int size) {
		this.size = size;
	}

	@XmlAttribute(name="decimals")
	@XmlJavaTypeAdapter(type=Integer.class, value = IntegerAdapter.class)
	public Integer getDecimals() {
		return decimals;
	}

	public void setDecimals(Integer decimals) {
		this.decimals = decimals;
	}

	@XmlAttribute(name="type")
	public String getType() {
		if (type == null)
			return "none";
		return type.getName();
	}

	public void setType(String typeName) throws InvalidParameterException {
		// lookup in the data dictionary for the type definition f
		DataType dt = Table.getCurrentTable().getDictionary().getType(typeName.toLowerCase());
		if (dt == null) {
			throw new InvalidParameterException("Column type '"+typeName+"' is unsupported");
		}
		this.type = dt;
	}

	/**
	 * Label is a descriptive name for the column
	 * 
	 * @return
	 */
	public String getLabel() {
		return label;
	}

	public void setLabel(String label) {
		this.label = label;
	}

	/**
	 * Name is the actual field/column name used in the storage system
	 * 
	 * @return
	 */
	@XmlAttribute(required=true)
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getDefaultValue() {
		return defaultValue;
	}

	@XmlElement(name="defaultValue",nillable=true,defaultValue="\u0000")
	public void setDefaultValue(String defValue) {
		this.defaultValue = defValue;
	}

	/**
	 * Table is a reference to the table where the column is defined.
	 * 
	 * @return
	 */
	@XmlTransient
	public Table getTable() {
		return table;
	}

	public void setTable(Table table) {
		this.table = table;
	}
	
	public Boolean isSigned() {
		if (type == null) return false;
		return type.isSigned();
	}

	/**
	 * Signed attribute is associated to the data type "signed" attribute.
	 * Columns can't have a "signed" attribute different from their data
	 * type's "signed" attribute.
	 * 
	 * @param signed
	 */
	@XmlAttribute(name="signed")
	@XmlJavaTypeAdapter(type=Boolean.class, value = BooleanAdapter.class)
	public void setSigned(Boolean signed) {
		type.setSigned(signed);
	}

	/** 
	 * Numeric attribute is defined by the datatype
	 * @return
	 */
	public boolean isNumeric() {
		if (type == null) return false;
		return type.isNumeric();
	}

	@Override
	public String toString() {
		return "Column "+getName()+":"+getType()+":"+getSize()+","+getDecimals()+":"+isNullable()+":"+getLabel();
	}

	@XmlAttribute(name="required")
	@XmlJavaTypeAdapter(type=Boolean.class, value = BooleanAdapter.class)
	public Boolean isRequired() {
		return required;
	}

	public void setRequired(Boolean required) {
		this.required = required;
	}

	@XmlTransient
	public boolean isPrimaryKey() {
		return primaryKey;
	}

	public void setPrimaryKey(boolean primaryKey) {
		this.primaryKey = primaryKey;
	}

}
